/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <isa.h>

/* We use the POSIX regex functions to process regular expressions.
 * Type 'man regex' for more information about POSIX regex functions.
 */
#include <regex.h>

enum {
  TK_NOTYPE = 256, 
  TK_EQ,
  TK_NEQ,
  TK_AND,
  TK_OR,
  TK_NUM,
  TK_HEXNUM,
  TK_REGNAME,
  TK_POINTER, 
};

static struct rule {
  const char *regex;
  int token_type;
} rules[] = {

  /* TODO: Add more rules.
   * Pay attention to the precedence level of different rules.
   */

  {" +", TK_NOTYPE},    // spaces
  {"\\+", '+'},         // plus
  {"==", TK_EQ},        // equal
	{"\\-", '-'},					//minus
	{"\\*", '*'},					//multiply
	{"\\/", '/'},					//divide

	{"!=", TK_NEQ},					// not equal

	{"&&",TK_AND},						//and
	{"\\|\\|",TK_OR},					//or
	{"!",'!'},						//not

	{"\\(", '('},
	{"\\)", ')'},

  {"0[xX][0-9a-fA-F]+", TK_HEXNUM},		//hexadecimal number
	{"[0-9]+", TK_NUM},				//decimal number
	
	{"\\$[a-z][0-9]",TK_REGNAME},		//register name	
  {"\\*[a-z][A-Z][0-9]",TK_POINTER}


};

#define NR_REGEX ARRLEN(rules)

static regex_t re[NR_REGEX] = {};

/* Rules are used for many times.
 * Therefore we compile them only once before any usage.
 */
void init_regex() {
  int i;
  char error_msg[128];
  int ret;

  for (i = 0; i < NR_REGEX; i ++) {
    ret = regcomp(&re[i], rules[i].regex, REG_EXTENDED);
    if (ret != 0) {
      regerror(ret, &re[i], error_msg, 128);
      panic("regex compilation failed: %s\n%s", error_msg, rules[i].regex);
    }
  }
}

typedef struct token {
  int type;
  char str[128];
} Token;

static Token tokens[32] __attribute__((used)) = {};
static int nr_token __attribute__((used))  = 0;

static bool make_token(char *e) {
  int position = 0;
  int i;
  regmatch_t pmatch;

  nr_token = 0;

  while (e[position] != '\0') {
    /* Try all rules one by one. */
    for (i = 0; i < NR_REGEX; i ++) {
      if (regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
        char *substr_start = e + position;
        int substr_len = pmatch.rm_eo;

        Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s",
            i, rules[i].regex, position, substr_len, substr_len, substr_start);

        position += substr_len;
        switch (rules[i].token_type) {
          case TK_NOTYPE:
						break;
					case '+':
					case '-':
					case '*':
					case '/':
					case TK_EQ:
					case TK_NEQ:
					case TK_AND:
					case TK_OR:
					case '!':
					case '(':
					case ')':
					case TK_NUM:
					case TK_HEXNUM:
					case TK_REGNAME:
          case TK_POINTER:{
						tokens[nr_token].type=rules[i].token_type;
						strncpy(tokens[nr_token++].str, substr_start, substr_len);
						break;
					}

          default:{
            assert(0);
            printf("Failed to recognize the symbol");
          } break;
        }

        break;
      }
    }

    if (i == NR_REGEX) {
      printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
      return false;
    }
  }

  return true;
}
int level(int type){
  switch (type)
  {
    case TK_AND:return 0;break;
    case TK_OR:return 0;break;
    case TK_EQ:return 1;break;
    case TK_NEQ:return 1;break;
    case '+':return 2;break;
    case '-':return 2;break;
    case '*':return 3;break;
    case '/':return 3;break;
    case '!':return 4;break;
    default:return 10;break;
    break;
  }
}
int main_operators(int p,int q){
  int flag=0;
  int op=0;
  while (p<q)
  {
    if(strcmp(tokens[p].str,")")==0) flag --;
    if(strcmp(tokens[p].str,"(")==0) flag ++;
    if(flag ==0){
      if(level(tokens[p].type)<level(tokens[op].type)){
        op=p;
      }
    }
    p ++;
  }
  return op;
}
bool check_parentheses(int p,int q){
  int flag_p=0;
  int flag=0;
  if(strcmp(tokens[p].str,"(") ||  strcmp(tokens[q].str,")") ){
    return false;
  }
  else{
    while (p<= q)
    { 
      if (flag>0)
      {
        flag_p=1;
      }
      
      if(strcmp(tokens[p].str,"(")==0){
        flag ++;
      }
      else if(strcmp(tokens[p].str,")")==0){
        flag --;
      }
      if ((flag_p!=0)  && (flag==0) &&(p!=q)) 
      {
        return false;
      }
      
      p++;
    }
    if(flag != 0){
      return false;
    }
    else{
      return true;   
    }
    
  }
}
word_t eval(int p,int q){
  if(p > q){
    printf("this is error");
    assert(0);
  }
  else if(p == q){
    if(tokens[p].type==TK_NUM){
      return  strtoul(tokens[p].str, NULL, 10);
    }
    else if(tokens[p].type==TK_HEXNUM){
      return  strtoul(tokens[p].str, NULL, 16);
    }
    else if(tokens[p].type==TK_REGNAME){
      word_t res=0;
      bool success;
      isa_reg_str2val(tokens[p].str+1,&success,&res);
      return res;
    }
    else{
      printf("this is error");
      assert(0);
    }
  }
  else if(check_parentheses(p, q) == true){
    return eval(p+1,q-1);
  }
  else {
    word_t val1=0;
    int op = main_operators(p,q);
    if(strcmp(tokens[p].str,"!")){
       val1 = eval(p,op-1);
    }
    word_t val2 = eval(op+1,q);
    switch (tokens[op].type)
    {
      case '+':return val1 + val2;break;
      case '-':return val1 - val2;break;
      case '*':return val1 * val2;break;
      case '/':return val1 / val2;break;
      case TK_EQ:return val1 == val2 ? 1:0;break;
      case TK_NEQ:return val1 == val2 ? 0: 1;break;
      case TK_AND:return val1 && val2;break;
      case TK_OR:return val1 || val2;break;
      case '!':return !val2;break;
      default:assert(0); break;
        break;
    }
  }
  return 0;
}
word_t expr(char *e, bool *success) {
  
  if (!make_token(e)) {
    *success = false;
    return 0;
  }
  else{
    *success = true; 
  }
   
  /* TODO: Insert codes to evaluate the expression. */
  word_t res = eval(0,nr_token-1);
  return res;
}
